package com;

import com.classUtil.ClassModifier;
import org.springframework.beans.BeansException;
import org.springframework.context.ApplicationContext;
import org.springframework.context.ApplicationContextAware;

import java.io.*;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.lang.reflect.Modifier;
import java.time.LocalDateTime;
import java.time.format.DateTimeFormatter;
import java.util.ArrayList;
import java.util.List;

/**
 * @author 大忽悠
 * @create 2023/1/11 13:14
 */
public class DynamicDebugExecutor implements ApplicationContextAware {
    /**
     * debug调试过程中产生的错误日志和返回结果输出到哪里
     */
    private static final DebugPrinter debugPrinter=DebugPrinter.getInstance();
    private static final DateTimeFormatter FORMATTER = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss");
    private static ApplicationContext applicationContext;

    public static void setSocketOutPutStream(OutputStream outPutStream){
        debugPrinter.setOutPutStream(outPutStream);
    }

    public static void execute(String classFilePath) {
        execute(readTestRemoteClass(classFilePath));
    }

    private static byte[] readTestRemoteClass(String classFilePath) {
        try (BufferedInputStream inputStream = new BufferedInputStream(new FileInputStream(classFilePath));
             ByteArrayOutputStream byteArrayOutputStream = new ByteArrayOutputStream()) {
            byte[] bytes = new byte[1024];
            int res = -1;
            while ((res = inputStream.read(bytes)) != -1) {
                byteArrayOutputStream.write(bytes, 0, res);
            }
            return byteArrayOutputStream.toByteArray();
        }catch (IOException e){
            throw new RuntimeException(e);
        }
    }

    public static void execute(byte[] classBytes) {
        Class<?> targetClass = HotSwapClassLoader.hotSwap(ClassModifier.modifyUTF8Constant(classBytes,"java/lang/System", "com/HackSystem"));
        debugPrinter.log("----------------------------------------------------------------------");
        debugPrinter.log("当前时间: " + LocalDateTime.now().format(FORMATTER));
        debugPrinter.log("正在执行远程动态debug调试中,测试类名为: " + targetClass.getName());
        List<Method> methodList = filterTargetMethods(targetClass);
        invokeTargetMethods(methodList);
    }

    private static void invokeTargetMethods(List<Method> methodList) {
        //执行这些debug方法
        for (Method method : methodList) {
            try {
                debugPrinter.log("正在执行测试方法: " + method.getName());
                debugPrinter.log("测试过程中输出内容如下:");
                debugPrinter.log("============================================");
                if(method.getParameters().length==0){
                    method.invoke(null);
                }else {
                    method.invoke(null,applicationContext);
                }
                debugPrinter.log("============================================");
            } catch (IllegalAccessException | InvocationTargetException e) {
                debugPrinter.print(e);
            }
        }
    }

    private static List<Method> filterTargetMethods(Class<?> targetClass) {
        //哪些方法会被视为debug方法呢?
        Method[] methods = targetClass.getMethods();
        List<Method> methodList = new ArrayList<>();
        for (Method method : methods) {
            //方法参数个数为0或者为applicationContext,并且方法为静态public方法
            if (Modifier.isStatic(method.getModifiers())&&Modifier.isPublic(method.getModifiers())) {
                if(method.getParameters().length==0){
                    methodList.add(method);
                }else if(method.getParameters().length==1 && method.getParameters()[0].getType().equals(ApplicationContext.class)){
                    methodList.add(method);
                }
            }
        }
        return methodList;
    }

    @Override
    public void setApplicationContext(ApplicationContext app) throws BeansException {
        applicationContext=app;
    }
}
